package com.gjy.util;

import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Minio 工具类
 *
 * @author 宫静雨
 * @version 1.0
 * @since 2022-09-20 15:09:59
 */
public class MinioUtil {

    private static final Logger logger = LoggerFactory.getLogger(MinioUtil.class);
    private static final MinioClient minioClient =
            ApplicationContextUtil.getBean("minio", MinioClient.class);

    /**
     * 判断一个 bucket 是否存在
     *
     * @param bucket bucket name
     * @return bucket是否存在
     */
    public static boolean existsBucket(String bucket) {
        try {
            BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucket).build();
            return minioClient.bucketExists(bucketExistsArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据 bucket 得到其中所有的文件名
     *
     * @param bucket bucket name
     * @return files name
     */
    public static List<String> listFileName(String bucket) {
        // 获取 bucket 中的所有元素
        Iterator<Result<Item>> iterator = minioClient.listObjects(ListObjectsArgs.builder()
                .recursive(true)
                .bucket(bucket).build())
                .iterator();
        ArrayList<String> strings = new ArrayList<>();

        while (iterator.hasNext()) {
            Result<Item> result = iterator.next();
            try {
                String item = result.get().objectName();
                strings.add(item);
            } catch (Exception e) {
                logger.error("列出桶: [{}]所有元素时失败, reason: {}",
                        bucket, e.getMessage());
            }
        }
        return strings;
    }

    /**
     * 占位符
     */
    private static final String BUCKET_PARAM = "${bucket}";

    /**
     * bucket权限-读写
     */
    private static final String READ_WRITE = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";


    /**
     * bucket权限-只读
     */
    private static final String WRITE_ONLY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + BUCKET_PARAM + "/*\"]}]}";

    /**
     * 创建 bucket
     *
     * @param bucket bucket name
     */
    public static void createBucket(String bucket) {
        try {
            boolean exists = existsBucket(bucket);
            if (!exists) {
                MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder()
                        .bucket(bucket)
                        .build();
                minioClient.makeBucket(makeBucketArgs);
                SetBucketPolicyArgs policyArgs = SetBucketPolicyArgs.builder()
                        .bucket(bucket)
                        .config(READ_WRITE.replace(BUCKET_PARAM, bucket))
                        .build();
                minioClient.setBucketPolicy(policyArgs);
                logger.info("创建桶成功,桶的名字为: {}", bucket);
            } else {
                logger.warn("桶已存在: {}", bucket);
            }
        } catch (Exception e) {
            logger.error("创建桶失败: {}", e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 列出所有的bucket
     *
     * @return bucketInfos
     */
    public static List<BucketInfo> listBuckets() {
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            List<BucketInfo> collect = buckets.stream().map(bucket -> {
                BucketInfo bi = new BucketInfo();
                bi.setName(bucket.name());
                bi.setCreationDate(bucket.creationDate());
                return bi;
            }).collect(Collectors.toList());
            logger.info("获取到的 Buckets: {}", collect);
            return collect;
        } catch (Exception e) {
            logger.error("获取 buckets 失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 移除 bucket
     *
     * @param bucket bucket name
     * @return true/false
     */
    public static boolean removeBucket(String bucket) {
        try {
            boolean exists = existsBucket(bucket);
            if (exists) {
                // 先移除 bucket 中的所有元素
                List<String> bucketNames = listFileName(bucket);
                bucketNames.forEach(name -> {
                    try {
                        minioClient.removeObject(RemoveObjectArgs.builder()
                                .object(name)
                                .bucket(bucket).build());
                        logger.info("移除{} 中的元素: {} 成功", bucket, name);
                    } catch (Exception e) {
                        logger.info("移除{} 中的元素: {} 失败, reason: {}", bucket, name, e.getMessage());
                        e.printStackTrace();
                    }
                });
                // 再移除 bucket
                minioClient.removeBucket(RemoveBucketArgs.builder()
                        .bucket(bucket)
                        .build());
                logger.info("移除bucket: {} 成功", bucket);
            } else {
                logger.warn("桶不存在: {}", bucket);
            }
            return true;
        } catch (Exception e) {
            logger.error("移除bucket: {} 失败, reason: {}", bucket, e.getMessage());
            return false;
        }
    }

    @Data
    public static class BucketInfo implements Serializable {

        private String name;

        private ZonedDateTime creationDate;
    }
}